Skip to content

Data-bound chart components 3/8: plans and @reflex_xy.data - #463

Merged
masenf merged 3 commits into
stack/2-typed-handlesfrom
stack/3-plans-and-data-vars
Aug 7, 2026
Merged

Data-bound chart components 3/8: plans and @reflex_xy.data#463
masenf merged 3 commits into
stack/2-typed-handlesfrom
stack/3-plans-and-data-vars

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Stacked on #462. Base is stack/2-typed-handles.

The two halves the data-bound tier is made of. No wire and no component surface yet — those are PRs 4 and 5.

plan.py

A ChartPlan is a validated, data-free chart structure. Building one constructs the real xy tree from string channels, binds zero-row placeholder columns for every referenced channel through the production resolution path (a recording table, so the column list cannot drift from what binding will look up), and runs .figure() once — the full mark/config validation gate, in milliseconds, with no data ingestion.

The canonical JSON (plan_version: 1) is content-addressed into a digest and registered in a process-local map. Binding is the reverse: columns + plan → a fresh Chart (never reused) → .figure(), with mismatch errors that name both sides.

PLAN_VERSION and a golden digest are pinned, so accidental format churn fails a test rather than silently invalidating every mounted client's digest.

data_vars.py

@reflex_xy.data is the exact sibling of @reflex_xy.figure — same builder-targeted dependency tracking, pre-session short-circuit, underscore refusal, async dispatch, None-releases semantics — but its value is a DataHandle and what it publishes is columns.

The method's return annotation is the schema channel: a TypedDict parametrizes the handle, so PR5's factories can read column names without executing user code.

tokens.py, registry.py

Two families join xyv1: xyd1 for data tokens (same grammar and rebuild contract) and the composite xyp1|<digest>|<data token> naming a bound figure — plan digest for structure, data token for columns, both halves independently recoverable.

Column entries are their own kind of registry entry: immutable generations with no locks and no pins (a republish replaces the whole entry), swept under the existing TTL because the data method is the rebuild recipe. The data token → {digest} index and the on_error seam land here; PR4 wires them to the namespace.

Spec

reflex-integration.md §3.6 (data vars, plans, column entries, format stability), file map.

Test plan

  • uv run pytest tests/reflex_adapter tests/test_validation_timing.py — 200 passed
  • pre-commit run --all-files, ruff check, ruff format --check, ty check — clean

Review in cubic

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 300a9de1-41ee-4b19-8644-e978b2f131eb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces the server-side foundations for data-bound Reflex charts.

  • Adds typed synchronous and asynchronous @reflex_xy.data computed variables.
  • Adds validated, content-addressed chart plans and production-path column discovery.
  • Extends the registry with versioned column generations, dependent-plan rebuilding, and error fan-out.
  • Defines data and composite plan token formats and documents the new architecture.

Confidence Score: 5/5

The PR appears safe to merge based on the eligible follow-up findings.

No blocking failure remains in the eligible follow-up review scope.

Important Files Changed

Filename Overview
python/reflex_xy/data_vars.py Adds typed data computed variables, column validation, deterministic token minting, publication, release semantics, and async dispatch.
python/reflex_xy/plan.py Adds immutable content-addressed chart plans with zero-row validation, recorded column bindings, and fresh-chart binding.
python/reflex_xy/registry.py Adds versioned column entries, dependent-plan indexing and rebuilding, generation ordering guards, error delivery, and TTL sweeping.
python/reflex_xy/tokens.py Adds data-token and composite-plan-token grammars while preserving deterministic state-token parsing.
python/reflex_xy/init.py Exposes the new data-variable decorator and computed-variable types through the public package API.
spec/design/reflex-integration.md Documents data variables, plans, composite identities, column-entry lifecycle, ordering, and format stability.
tests/reflex_adapter/test_data_var.py Covers typed schema propagation, dependency tracking, publication, release, generation ordering, validation, and TTL behavior.
tests/reflex_adapter/test_plan.py Covers plan validation, stable addressing, immutable snapshots, fresh binding, diagnostics, and Tailwind inventory collection.

Reviews (3): Last reviewed commit: "fix(reflex): generation-gated column rep..." | Re-trigger Greptile

@codspeed-hq

codspeed-hq Bot commented Aug 5, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 109 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing stack/3-plans-and-data-vars (9fd4362) with stack/2-typed-handles (d0886d6)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on stack/2-typed-handles (3e98589) during the generation of this report, so 4221648 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread python/reflex_xy/registry.py Outdated
Comment thread python/reflex_xy/plan.py
Comment thread python/reflex_xy/tokens.py
Comment thread python/reflex_xy/registry.py
Comment thread python/reflex_xy/data_vars.py Outdated
Comment thread tests/reflex_adapter/test_plan.py
@FarhanAliRaza
FarhanAliRaza force-pushed the stack/3-plans-and-data-vars branch 2 times, most recently from 4b1f770 to 9fd4362 Compare August 6, 2026 13:40
@FarhanAliRaza FarhanAliRaza changed the title Data-bound chart components 3/7: plans and @reflex_xy.data Data-bound chart components 3/8: plans and @reflex_xy.data Aug 6, 2026
@FarhanAliRaza

Copy link
Copy Markdown
Contributor Author

Review addressed in 9fd4362:

  • Stale-generation publish (P1): every dependent rebuild now carries the ColumnEntry generation it serves, and each outcome — the figure publish and the failure release + err {resync} — is gated on that generation still being current atomically with the registry mutation (publish's locked body factored into _publish_locked for that). An older republish that finishes late is dropped whole; a superseded failure raises no stale error. Pinned by test_data_var.py::test_stale_column_generation_cannot_overwrite_a_newer_publish.
  • Mutable plans (P2): build_plan deep-snapshots children + chart props before hashing and registers the snapshot — mutating a reused mark node can no longer change binding behavior behind an unchanged digest. Pinned by test_plan.py::test_plan_is_a_snapshot_immune_to_later_node_mutation.

Spec: reflex-integration.md "Republish ordering" / "Plan immutability".

masenf
masenf previously approved these changes Aug 6, 2026
FarhanAliRaza and others added 3 commits August 6, 2026 21:59
The two halves the data-bound component tier is made of, with no wire or
component surface yet.

plan.py — a ChartPlan is a validated, data-free chart structure. Building
one constructs the real xy tree from string channels, binds zero-row
placeholder columns for every referenced channel through the production
resolution path (a recording table, so the column list cannot drift from
what binding will look up), and runs .figure() once: the full mark/config
validation gate, in milliseconds, with no data ingestion. The canonical
JSON (plan_version: 1) is content-addressed into a digest and registered in
a process-local map; binding is columns + plan -> a fresh Chart -> figure,
with mismatch errors that name both sides. PLAN_VERSION and a golden digest
are pinned so accidental format churn fails a test rather than silently
invalidating every mounted client's digest.

data_vars.py — @reflex_xy.data is the exact sibling of @reflex_xy.figure:
same builder-targeted dependency tracking, pre-session short-circuit,
underscore refusal, async dispatch, and None-releases semantics, but its
value is a DataHandle and what it publishes is columns. The method's return
annotation is the schema channel: a TypedDict parametrizes the handle, so
the layer above can read column names without executing user code.

tokens.py grows two families beside xyv1: xyd1 for data tokens (same
grammar and rebuild contract) and the composite xyp1|<digest>|<data token>
that names a bound figure — plan digest for the structure, data token for
the columns, both halves independently recoverable.

registry.py stores column entries as their own kind: immutable generations
with no locks and no pins (a republish replaces the whole entry), swept
under the existing TTL because the data method is the rebuild recipe. The
data-token -> {digest} index and the on_error seam land here; the namespace
wires them in the next change.

Spec: reflex-integration.md §3.6 (data vars, plans, column entries, format
stability), file map.
Two review P-findings on the plan/data tier:

- publish_columns rebuilds run outside the registry mutex, so two
  republishes of one data token could finish in reverse order and leave
  subscribers on stale pixels. Each rebuild now carries its ColumnEntry
  generation and every outcome (figure publish, failure release + err
  frame) is gated on that generation still being current, atomically
  with the registry mutation (publish's locked body factored into
  _publish_locked for that).
- build_plan hashes and registers a deep snapshot of children and chart
  props: mutating a reused mark node after the factory call can no
  longer change binding behavior behind an unchanged digest.
data_vars.py duplicated vars.py's _builder_target verbatim; a fix landing
in one copy would silently miss the other. The data tier now imports the
figure tier's helper — one body for the one behavior, matching the
'exact sibling' contract the module documents.
@masenf
masenf force-pushed the stack/3-plans-and-data-vars branch from 9fd4362 to 794dae6 Compare August 6, 2026 22:08

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile has paused reviews on this repository — it used its 100 free open-source review credits for this billing period. Reviews resume automatically on September 3. To continue before then, an organization admin can keep reviews running past the free credits — those bill as normal usage.

@masenf
masenf merged commit da17abc into main Aug 7, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants